package com.gitee.wsl.ext.number

import com.gitee.wsl.base.ValueRange

fun Float.normalize(fromMin: Float, fromMax: Float, toMin: Float, toMax: Float): Float {

    // [min,max] to [a,b] >>> f(x) = (b - a) (x - min) / (max - min) + a
    val v = when {
        this > fromMax -> fromMax
        this < fromMin -> fromMin
        else -> this
    }
    return (toMax - toMin) * (v - fromMin) / (fromMax - fromMin) + toMin
}


//fun Double.normalizeZero(): Double = if (this.isAlmostZero()) 0.0 else this
private val MINUS_ZERO_D = -0.0
private val MINUS_ZERO_F = -0.0f

fun Double.normalizeZero(): Double = if (this == MINUS_ZERO_D) 0.0 else this

fun Float.normalizeZero(): Float = if (this == MINUS_ZERO_F) 0f else this


/**
 * Returns this value with the specified [sourceRange]
 * mapped to the [ClosedRange] `0.0..1.0`.
 *
 * @throws IllegalArgumentException if this value isn't in the specified [sourceRange]
 */
@Suppress("NOTHING_TO_INLINE")
 inline fun Double.normalize(
    sourceRange: ClosedRange<Double>,
): Double = map(ValueRange.Normalized.max, ValueRange.Normalized.min, sourceRange.start, sourceRange.endInclusive)

/**
 * Returns this value with the specified [sourceRangeStart] (default: `0.0`) and [sourceRangeEndInclusive]
 * mapped to the [ClosedRange] `0.0..1.0`.
 *
 * @throws IllegalArgumentException if this value isn't in the [ClosedRange]
 * starting with the specified [sourceRangeStart] and
 * ending with the specified [sourceRangeEndInclusive]
 */
@Suppress("NOTHING_TO_INLINE")
 inline fun Double.normalize(
    sourceRangeEndInclusive: Double,
    sourceRangeStart: Double = ValueRange.Normalized.start,
): Double = map(ValueRange.Normalized, sourceRangeStart..sourceRangeEndInclusive)

/**
 * Returns this value with the specified [sourceRange]
 * mapped to the [ClosedRange] `0.0..1.0`.
 *
 * @throws IllegalArgumentException if this value isn't in the specified [sourceRange]
 */
@Suppress("NOTHING_TO_INLINE")
 inline fun Int.normalize(
    sourceRange: ClosedRange<Int>,
): Double = toDouble().map(
    destinationRangeStart = ValueRange.Normalized.min,
    destinationRangeEndInclusive = ValueRange.Normalized.max,
    sourceRangeStart = sourceRange.start.toDouble(),
    sourceRangeEndInclusive = sourceRange.endInclusive.toDouble(),
)

/**
 * Returns this value with the specified [sourceRangeStart] (default: `0.0`) and [sourceRangeEndInclusive]
 * mapped to the [ClosedRange] `0.0..1.0`.
 *
 * @throws IllegalArgumentException if this value isn't in the [ClosedRange]
 * starting with the specified [sourceRangeStart] and
 * ending with the specified [sourceRangeEndInclusive]
 */
@Suppress("NOTHING_TO_INLINE")
 inline fun Int.normalize(
    sourceRangeEndInclusive: Int,
    sourceRangeStart: Int = ValueRange.Normalized.min.toInt(),
): Double = toDouble().map(
    destinationRangeStart = ValueRange.Normalized.min,
    destinationRangeEndInclusive = ValueRange.Normalized.max,
    sourceRangeStart = sourceRangeStart.toDouble(),
    sourceRangeEndInclusive = sourceRangeEndInclusive.toDouble(),
)